package org.example.xmut.qa.adapter.in.web.exception;

import org.example.xmut.qa.adapter.in.web.dto.Result;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.stream.Collectors;

/**
 * 全局异常处理器
 * 统一处理应用中的各种异常，避免异常直接暴露给用户
 *
 * @Slf4j Lombok注解，自动生成日志对象log，用于记录异常日志
 * @RestControllerAdvice 组合注解，相当于@ControllerAdvice + @ResponseBody
 * 用于定义全局异常处理器，可以捕获控制器层抛出的异常
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 处理认证异常
     *
     * 当用户认证失败时（如Token无效、过期等）会抛出AuthenticationException
     *
     * @ExceptionHandler 注解指定该方法处理哪种异常
     * @ResponseStatus 注解指定返回的HTTP状态码
     *
     * @param e 认证异常对象
     * @return Result<Void> 统一响应结果
     */
    @ExceptionHandler(AuthenticationException.class)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public Result<Void> handleAuthenticationException(AuthenticationException e) {
        log.error("认证异常: {}", e.getMessage());
        return Result.unauthorized("认证失败: " + e.getMessage());
    }

    /**
     * 处理凭据错误异常
     *
     * 当用户名或密码错误时会抛出BadCredentialsException
     * 这是AuthenticationException的一个子类
     *
     * @param e 凭据错误异常对象
     * @return Result<Void> 统一响应结果
     */
    @ExceptionHandler(BadCredentialsException.class)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public Result<Void> handleBadCredentialsException(BadCredentialsException e) {
        log.error("凭据错误: {}", e.getMessage());
        return Result.unauthorized("用户名或密码错误");
    }

    /**
     * 处理访问拒绝异常
     *
     * 当已认证用户尝试访问没有权限的资源时会抛出AccessDeniedException
     *
     * @param e 访问拒绝异常对象
     * @return Result<Void> 统一响应结果
     */
    @ExceptionHandler(AccessDeniedException.class)
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public Result<Void> handleAccessDeniedException(AccessDeniedException e) {
        log.error("访问拒绝: {}", e.getMessage());
        return Result.forbidden("访问被拒绝，权限不足");
    }

    /**
     * 处理参数校验异常 - @Valid注解
     *
     * 当使用@Valid注解验证请求参数失败时会抛出MethodArgumentNotValidException
     * 例如LoginRequest中的用户名或密码不符合验证规则
     *
     * @param e 参数校验异常对象
     * @return Result<Void> 统一响应结果
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<Void> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        // 收集所有字段的错误信息并拼接成字符串
        String errorMessage = e.getBindingResult().getFieldErrors().stream()
                .map(FieldError::getDefaultMessage)
                .collect(Collectors.joining(", "));
        log.error("参数校验失败: {}", errorMessage);
        return Result.error("参数校验失败: " + errorMessage);
    }

    /**
     * 处理参数绑定异常
     *
     * 当请求参数绑定到对象时发生错误会抛出BindException
     *
     * @param e 参数绑定异常对象
     * @return Result<Void> 统一响应结果
     */
    @ExceptionHandler(BindException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<Void> handleBindException(BindException e) {
        // 收集所有字段的错误信息并拼接成字符串
        String errorMessage = e.getFieldErrors().stream()
                .map(FieldError::getDefaultMessage)
                .collect(Collectors.joining(", "));
        log.error("参数绑定失败: {}", errorMessage);
        return Result.error("参数绑定失败: " + errorMessage);
    }

    /**
     * 处理约束违反异常
     *
     * 当使用Bean Validation API验证失败时会抛出ConstraintViolationException
     *
     * @param e 约束违反异常对象
     * @return Result<Void> 统一响应结果
     */
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<Void> handleConstraintViolationException(ConstraintViolationException e) {
        // 收集所有约束违反的错误信息并拼接成字符串
        String errorMessage = e.getConstraintViolations().stream()
                .map(ConstraintViolation::getMessage)
                .collect(Collectors.joining(", "));
        log.error("约束违反: {}", errorMessage);
        return Result.error("参数校验失败: " + errorMessage);
    }

    /**
     * 处理非法参数异常
     *
     * 当传递给方法的参数不合法时会抛出IllegalArgumentException
     *
     * @param e 非法参数异常对象
     * @return Result<Void> 统一响应结果
     */
    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<Void> handleIllegalArgumentException(IllegalArgumentException e) {
        log.error("非法参数: {}", e.getMessage());
        return Result.error("参数错误: " + e.getMessage());
    }

    /**
     * 处理空指针异常
     *
     * 当尝试访问空对象的属性或方法时会抛出NullPointerException
     *
     * @param e 空指针异常对象
     * @return Result<Void> 统一响应结果
     */
    @ExceptionHandler(NullPointerException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Result<Void> handleNullPointerException(NullPointerException e) {
        log.error("空指针异常", e);
        return Result.error("系统内部错误，请联系管理员");
    }

    /**
     * 处理运行时异常
     *
     * 当发生未预期的运行时错误时会抛出RuntimeException
     * 这是很多异常的父类
     *
     * @param e 运行时异常对象
     * @return Result<Void> 统一响应结果
     */
    @ExceptionHandler(RuntimeException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Result<Void> handleRuntimeException(RuntimeException e) {
        log.error("运行时异常: {}", e.getMessage(), e);
        return Result.error("系统异常: " + e.getMessage());
    }

    /**
     * 处理其他所有异常
     *
     * 作为兜底的异常处理方法，处理所有未被上面方法处理的异常
     *
     * @param e 异常对象
     * @return Result<Void> 统一响应结果
     */
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Result<Void> handleException(Exception e) {
        log.error("未知异常: {}", e.getMessage(), e);
        return Result.error("系统内部错误，请联系管理员");
    }

    /**
     * 自定义业务异常类
     *
     * 用于处理应用程序中特定的业务异常
     * 继承自RuntimeException，是一个受检异常
     */
    public static class BusinessException extends RuntimeException {
        /**
         * 异常码
         *
         * 用于标识异常的类型，便于前端进行不同的处理
         */
        private final int code;

        /**
         * 构造函数 - 只有消息
         *
         * @param message 异常消息
         */
        public BusinessException(String message) {
            super(message);
            this.code = 500;
        }

        /**
         * 构造函数 - 有码和消息
         *
         * @param code 异常码
         * @param message 异常消息
         */
        public BusinessException(int code, String message) {
            super(message);
            this.code = code;
        }

        /**
         * 获取异常码
         *
         * @return int 异常码
         */
        public int getCode() {
            return code;
        }
    }

    /**
     * 处理自定义业务异常
     *
     * 处理应用程序中抛出的BusinessException
     *
     * @param e 业务异常对象
     * @return Result<Void> 统一响应结果
     */
    @ExceptionHandler(BusinessException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<Void> handleBusinessException(BusinessException e) {
        log.error("业务异常: {}", e.getMessage());
        return Result.error(e.getCode(), e.getMessage());
    }
}
